﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Collections;

namespace PluralSightTut
{
    public class MyCustomException : Exception
    {
        public MyCustomException(string message) : base(message)
        {

        }
    }
    class Tester
    {
        public int DoDivide(int x, int y)
        {
            if (x == 0)
                throw new MyCustomException("Dividend Can't be Zero");
            try
            {
                return x / y;
            }
            catch (Exception exp)
            {
                throw new Exception(exp.Message);
            }
        }
    }
    class Program
    {
        public delegate void VoidDelegate(string name);
        public delegate int IntDelegate();
        public delegate string StringDelegate();

        static void Main(string[] args)
        {

            //var sotreObject = new List<Storable>();

            //Note note = new Note();
            //sotreObject.Add(note);

            //DBEntry db = new DBEntry();
            //sotreObject.Add(db);

            //foreach (Storable str in sotreObject)
            //{
            //    str.
            //}
            Worker emp1 = new Worker();
            
            emp1.Name = "Worker1";
            emp1.Salary = 10.000;
            Console.WriteLine("{0} {1} {2} {3} {4} {5}", 
                               
                               emp1.AbstractMethodofEmployee(), 
                               emp1.VirtualMethodofEmployee(), 
                               emp1.MemeberMethodofEmployee(emp1.Name,emp1.Salary), 
                               emp1.InterfaceMethodWrite(),
                               emp1.InterfaceMethodRead(),
                               emp1.OtherMethodofWorker()
                             );

            Console.WriteLine("\n");
            Manager emp2 = new Manager();
            emp2.Name = "Manager1";
            emp2.Salary = 20.000;
            Console.WriteLine("{0} {1} {2} {3} {4} {5}", 
                
                               emp2.AbstractMethodofEmployee(), 
                               emp2.VirtualMethodofEmployee(), 
                               emp2.MemeberMethodofEmployee(emp2.Name,emp2.Salary), 
                               emp2.InterfaceMethodWrite(),
                               emp2.InterfaceMethodRead(), 
                               emp2.OtherMethodofManager()
                             );


            var company = new List<Employee>();
            company.Add(emp1);
            company.Add(emp2);

            Console.WriteLine("\n\n***** Foreach loop for abstract Class ");
            foreach (Employee emp in company)
            {
                Console.WriteLine("{0} {1} {2}", emp.AbstractMethodofEmployee(), emp.VirtualMethodofEmployee(), emp.MemeberMethodofEmployee(emp.Name, emp.Salary));
            }

            var storable = new List<Storable>();
            storable.Add(emp1);
            storable.Add(emp2);

            Console.WriteLine("\n\n***** Foreach loop for Interface ");
            foreach (Storable str in storable)
            {
                Console.WriteLine("{0} {1}", str.InterfaceMethodWrite(), str.InterfaceMethodRead());
            }
            //Console.WriteLine("Manager Salary : {0} WOrker Salary : {1}", emp1.GiveRise(), emp2.GiveRise());


            
            
            //t.Method1();
            //Appartment apt = new Appartment();
            //apt.ID = 1011;

            //Area apt2 = new Appartment();
            //apt2.ID = 101;

            //Appartment apt3 = apt2 as Appartment;
            //apt3.Name = "Segunbagicha";
            //apt3.RoadID = 16;
            //apt3.BuildingID = 2;
            //apt3.AppartmentName = "Eastern Morshed";
            //apt3.PrintArea(apt3.ID);
            //Console.WriteLine("********************************");
            //apt2.PrintArea(apt2.ID);

            //Area ar = Area.GetArea();

            //var Area = new Area[5];
            //Random r = new Random();

            //for (int i = 0; i < Area.Length; i++)
            //{
            //    Area[i] = new Area(i+1);
            //}



            //foreach (Area areas in Area)
            //{
            //    Console.WriteLine(areas.ID);
            //}

            //Console.WriteLine(apt3.ID);
            //Area.GetArea();
            //foreach (var areas in Area.GetArea())
            //{
            //    Console.WriteLine(areas.Name);
            //}

            //Dictionary<int, Area> dict = new Dictionary<int, Area>();
            //foreach (var areas in Area.GetArea())
            //{
            //    dict.Add(areas.ID, areas);
            //    //Console.WriteLine(areas.Name);
            //}

            //Console.WriteLine(dict[2].Name);
            //int x = 12 / 0;
            Console.WriteLine("\n\n\n ********************** Custom Exception ************ ");
            Tester t = new Tester();
            try
            {
                Console.WriteLine(t.DoDivide(18, 0));
                Console.WriteLine(t.DoDivide(0, 9));
                Console.WriteLine(t.DoDivide(18, 9));
            }
            catch (MyCustomException exp)
            {
                Console.WriteLine(exp.Message);
            }
            catch (Exception exp)
            {
                Console.WriteLine(exp.Message);
            }
            finally
            {
                try
                {
                    Console.WriteLine(t.DoDivide(0, 9));
                }
                catch (MyCustomException exp)
                {
                    Console.WriteLine(exp.Message);
                }
            }


            Console.WriteLine("\n\n\n ********************** Delegate ************ ");
            VoidDelegate voidDelegate;// = new VoidDelegate(EventDelegates.Subscirber1);
            voidDelegate = EventDelegates.Subscirber1;
            voidDelegate("Subsriber1");
            voidDelegate = EventDelegates.Subscriber2;
            voidDelegate("Subsriber2");

            IntDelegate intDelegate;
            intDelegate = EventDelegates.Subsciber3;
            Console.WriteLine("Subsciber"+intDelegate());

            StringDelegate strDelegate;
            strDelegate = EventDelegates.Subsciber4;
            Console.WriteLine(strDelegate());


            Console.WriteLine("\n\n\n ********************** Delegate with Non Static Function ************ ");
            EventDelegates evnt = new EventDelegates("Siam");
            voidDelegate = new VoidDelegate(evnt.Subscirber1NonStatic);
            voidDelegate("");
            EventDelegates evnt2 = new EventDelegates();
            voidDelegate += evnt2.Subscriber2NonStatic;
            voidDelegate("New name");

            Console.WriteLine("\n\n\n ********************** Delegate with DIfferent Class Function ************ ");
            Worker evnt3 = new Worker();
            Manager envt4 = new Manager();
            strDelegate = new StringDelegate(evnt3.AbstractMethodofEmployee);
            strDelegate += evnt3.VirtualMethodofEmployee;
            strDelegate += envt4.AbstractMethodofEmployee;
            strDelegate += envt4.VirtualMethodofEmployee;
            string all = strDelegate();
            Console.WriteLine(all);


            Console.WriteLine("\n\n\n ********************** Static Constructor ************ ");
            // Constructor are two types : Instance and Static
            // Static Constructor will always executed firstbefore any instance ctor. 
            // If a class has multiple instance Ctor, the Static will execute first.
            // Static Ctor will execute only once during the life of a class
            // A class can have at-most one Static Ctor. They cannot be overloaded, where Instance Ctor can be ( like defauly Ctor or Parametarized Ctor)
            // Basically they are used to initialize static variables
            // Static Ctor does not have any access modifier (they are invoked at run-time), where instane Ctor have Public
            Console.WriteLine(StaticCtor.totalCount);
            StaticCtor ctor = new StaticCtor();
            Console.WriteLine(StaticCtor.totalCount + "  " + ctor.Name);
            ctor = new StaticCtor("Siam");
            Console.WriteLine(StaticCtor.totalCount + "  " + ctor.Name);


            Console.WriteLine("\n\n\n ********************** Return Statement ************ ");
            // A function with return type void can still have a return statement. This will mean that
            // it will get back to place from where it was called
            Console.WriteLine("Before Function Call");
            callFunc(2);
            Console.WriteLine("After Function Call");


            Console.WriteLine("\n\n\n ********************** Exceptions ************ ");
            // A function can throw many type of exceptions (NotImplementation, DivideByZero etc)
            // To handle exceptions we will define functions into try block
            // and those exceptions will be cought by catch block. A try block can have multiple catch statements but only one catch will be executed
            // If we don't know what exception type, the compiler will try to match with the smallest possible match within those catch blocks
            try
            {
                DoSomeWork(3); // change paramater of DoSomeWork from 2 to 5 and different Exception will be thrown
            }
            catch (NotImplementedException exp)
            {
                Console.WriteLine("Not Implementation Exception : " + exp.Message + " Occured from : " + exp.TargetSite + " Stack Trace : " + exp.StackTrace);
            }
            catch (DivideByZeroException exp)
            {
                Console.WriteLine("Divide by zero Exception : " + exp.Message + " Occured from : " + exp.TargetSite + " Stack Trace : " + exp.StackTrace);
            }
            catch (IndexOutOfRangeException exp)
            {
                Console.WriteLine("Index out of range Exception : " + exp.Message + " Occured from : " + exp.TargetSite + " Stack Trace : " + exp.StackTrace);
            }
            catch (Exception exp) 
            {
                // This is the most derived type exception. Exception can catch any type of exceptions (except StackOverFlowException), 
                // because it's the base class for every other Exception Classes (DivideByZero, NotImplementation, NullReferenceException etc)
                Console.WriteLine("Global Exception : " + exp.Message + " Occured from : " + exp.TargetSite);
            }
            // If we don't handle the exception, our program will be terminated. If we handle it, rest of the program 
            // after the exception will be executed. Here, the below Console.WriteLine() will be executed
            Console.WriteLine("After Exception");


            Console.WriteLine("\n\n\n ********************** Generic Classes ************ ");
            GenericClass<GenericInput1> genericClass = new GenericClass<GenericInput1>();
            genericClass.Addd(new GenericInput1 { Name = "Yeasin" });
            genericClass.Addd(new GenericInput1 { Name = "Abedin" });
            genericClass.Addd(new GenericInput1 { Name = "Siam" });


            GenericClass<GenericInput2> genericClass2 = new GenericClass<GenericInput2>();
            genericClass2.Addd(new GenericInput2 { Address = "Yeasin" });
            genericClass2.Addd(new GenericInput2 { Address = "Abedin" });
            genericClass2.Addd(new GenericInput2 { Address = "Siam" });


            //foreach (GenericInput inpt in genericClass)
            //{
            //    Console.WriteLine(inpt.Name);
            //}

            GenericClass<string> genericString = new GenericClass<string>();
            genericString.Addd("String Yeasin");
            genericString.Addd("String Abedin");
            genericString.Addd("String Siam");


            GenericInput1 inp1 = new GenericInput1 { Name = "Dictionary Yeasin" };
            GenericInput1 inp2 = new GenericInput1 { Name = "Dictionary Abedin" };
            GenericInput1 inp3 = new GenericInput1 { Name = "Dictionary Siam" };

            GenericDictionary<string, GenericInput1> genericStringKey = new GenericDictionary<string, GenericInput1>();
            genericStringKey.Addd(inp1.Name, inp1);
            genericStringKey.Addd(inp2.Name, inp2);
            genericStringKey.Addd(inp3.Name, inp3);

            GenericDictionary<int, GenericInput1> genericIntKey = new GenericDictionary<int, GenericInput1>();
            genericIntKey.Addd(1, inp1);
            genericIntKey.Addd(2, inp2);
            genericIntKey.Addd(3, inp3);


        }

        private static void DoSomeWork(int p)
        {
            int x;
            if (p == 2)
                throw new NotImplementedException();
            if (p == 3)
            {
                x = 0;
                x = 2 / x;
            }
            if (p == 4)
            {
                int[] array = { 0, 1, 2, 3 };
                for (int i = 0; i < 5; i++)
                    Console.WriteLine(array[i]);

            }
            if (p == 5)
                throw new NullReferenceException();
        }

        private static void callFunc(int x)
        {
            if (x == 2)
                return; // this will just return to the caller. break out from the function
            else
                Console.WriteLine("In fucntion body");
        }

    }
}
